// 版权所有2009年围棋作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package doc

import (
	"go/ast"
	"go/token"
	"internal/lazyregexp"
	"sort"
	"strconv"
)

// ----------------------------------------------------------------------------
// 函数/方法集
// None
// 在内部，我们将函数视为方法，并将它们收集到方法集中。

// 方法集描述一组方法。Decl==nil的条目冲突
// 条目（在同一嵌入级别具有相同名称的多个方法）。
// None
type methodSet map[string]*Func

// recvString返回的recv的字符串表示形式
// 表格“T”、“*T”或“BADRECV”（如果不是合适的接收器类型）。
// None
func recvString(recv ast.Expr) string {
	switch t := recv.(type) {
	case *ast.Ident:
		return t.Name
	case *ast.StarExpr:
		return "*" + recvString(t.X)
	}
	return "BADRECV"
}

// set为f创建相应的Func并将其添加到mset。
// 如果有多个f具有相同的名称，则set将保留第一个f
// 一个有文件；冲突被忽略。布尔值
// 指定是否保持AST不变。
// None
func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) {
	name := f.Name.Name
	if g := mset[name]; g != nil && g.Doc != "" {
		// 已注册同名函数；
		// 因为它有文档，所以假设f只是另一个
		// 实现并忽略它。如果
		// 调用方正在使用go/build.ScanDir来确定
		// 实现包的文件。
		return
	}
	// 函数不存在或没有文档；使用f
	recv := ""
	if f.Recv != nil {
		var typ ast.Expr
		// 如果AST不正确，请小心
		if list := f.Recv.List; len(list) == 1 {
			typ = list[0].Type
		}
		recv = recvString(typ)
	}
	mset[name] = &Func{
		Doc:  f.Doc.Text(),
		Name: name,
		Decl: f,
		Recv: recv,
		Orig: recv,
	}
	if !preserveAST {
		f.Doc = nil // 单据已消耗-从AST中删除
	}
}

// 将方法m添加到方法集；如果方法设置为
// 已在相同或更高级别包含具有相同名称的方法
// 比m高一级。
// None
func (mset methodSet) add(m *Func) {
	old := mset[m.Name]
	if old == nil || m.Level < old.Level {
		mset[m.Name] = m
		return
	}
	if m.Level == old.Level {
		// 冲突-使用带有nil Decl的方法标记它
		mset[m.Name] = &Func{
			Name:  m.Name,
			Level: m.Level,
		}
	}
}

// ----------------------------------------------------------------------------
// 命名类型

// baseTypeName返回x（或“”）的基类型的名称
// 以及类型是否导入。
// None
func baseTypeName(x ast.Expr) (name string, imported bool) {
	switch t := x.(type) {
	case *ast.Ident:
		return t.Name, false
	case *ast.SelectorExpr:
		if _, ok := t.X.(*ast.Ident); ok {
			// 仅适用于限定类型名称；
			// 假设类型已导入
			return t.Sel.Name, true
		}
	case *ast.ParenExpr:
		return baseTypeName(t.X)
	case *ast.StarExpr:
		return baseTypeName(t.X)
	}
	return
}

// embeddedSet描述一组嵌入式类型。
type embeddedSet map[*namedType]bool

// namedType表示命名的非限定（本地或可能的）包
// 预先声明的）类型。类型名称的namedType始终通过找到
// reader.lookupType。
// None
type namedType struct {
	doc  string       // 类型的文档注释
	name string       // 类型名
	decl *ast.GenDecl // 如果尚未看到声明，则为零

	isEmbedded bool        // 如果嵌入此类型，则为true
	isStruct   bool        // 如果此类型是结构，则为true
	embedded   embeddedSet // 如果嵌入类型是指针，则为true

	// 关联声明
	values  []*Value // 常量和变量
	funcs   methodSet
	methods methodSet
}

// ----------------------------------------------------------------------------
// AST阅读器

// reader为单个包积累文档。
// 它修改AST:Comments（声明文档）
// 读卡器已收集的数据被设置为零
// 在各自的AST节点中，以便不打印它们
// 两次（打印文档时一次，打印时一次）
// 打印相应的AST节点）。
// None
type reader struct {
	mode Mode

	// 包属性
	doc       string // 包文件（如有）
	filenames []string
	notes     map[string][]*Note

	// 声明
	imports   map[string]int
	hasDotImp bool     // 如果设置，则包包含点导入
	values    []*Value // 常量和变量
	order     int      // const和var声明的排序顺序（当我们不能使用名称时）
	types     map[string]*namedType
	funcs     methodSet

	// 支持包本地错误类型声明
	errorDecl bool                 // 如果设置，则在本地声明类型“error”
	fixlist   []*ast.InterfaceType // 包含匿名字段“error”的接口列表
}

func (r *reader) isVisible(name string) bool {
	return r.mode&AllDecls != 0 || token.IsExported(name)
}

// lookupType返回具有给定名称的基类型。
// 如果尚未遇到基类型，则会出现新的
// 使用给定的名称键入，但没有关联的声明
// 将添加到类型映射中。
// None
func (r *reader) lookupType(name string) *namedType {
	if name == "" || name == "_" {
		return nil // 匿名类型没有类型文档
	}
	if typ, found := r.types[name]; found {
		return typ
	}
	// 找不到类型-添加一个不带声明的类型
	typ := &namedType{
		name:     name,
		embedded: make(embeddedSet),
		funcs:    make(methodSet),
		methods:  make(methodSet),
	}
	r.types[name] = typ
	return typ
}

// recordAnonymousField将fieldType注册为
// 父类型中的匿名字段。如果该字段已导入
// （限定名）或父项为零，则忽略该字段。
// 函数返回字段名。
// None
func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {
	fname, imp := baseTypeName(fieldType)
	if parent == nil || imp {
		return
	}
	if ftype := r.lookupType(fname); ftype != nil {
		ftype.isEmbedded = true
		_, ptr := fieldType.(*ast.StarExpr)
		parent.embedded[ftype] = ptr
	}
	return
}

func (r *reader) readDoc(comment *ast.CommentGroup) {
	// 按照惯例，只有一个包注释
	// 但是，如果有多个，则收集所有这些。
	text := comment.Text()
	if r.doc == "" {
		r.doc = text
		return
	}
	r.doc += "\n" + text
}

func (r *reader) remember(typ *ast.InterfaceType) {
	r.fixlist = append(r.fixlist, typ)
}

func specNames(specs []ast.Spec) []string {
	names := make([]string, 0, len(specs)) // 合理估计
	for _, s := range specs {
		// readValue保证它是一个*ast.ValueSpec
		for _, ident := range s.(*ast.ValueSpec).Names {
			names = append(names, ident.Name)
		}
	}
	return names
}

// readValue处理常量或变量声明。
// None
func (r *reader) readValue(decl *ast.GenDecl) {
	// 确定decl是否应与类型关联
	// 启发式：对于每个类型化条目，确定类型名称（如果有）。
	// 如果只有一个类型名足够
	// 频繁，将decl与相应类型关联。
	domName := ""
	domFreq := 0
	prev := ""
	n := 0
	for _, spec := range decl.Specs {
		s, ok := spec.(*ast.ValueSpec)
		if !ok {
			continue // 不应该发生，但要保守
		}
		name := ""
		switch {
		case s.Type != nil:
			// 存在一种类型；确定其名称
			if n, imp := baseTypeName(s.Type); !imp {
				name = n
			}
		case decl.Tok == token.CONST && len(s.Values) == 0:
			// 不存在类型或值，但我们有一个常量声明；
			// 使用以前的类型名称（可能是空字符串）
			name = prev
		}
		if name != "" {
			// 条目具有命名类型
			if domName != "" && domName != name {
				// 多个类型名称-不关联
				// 任何类型
				domName = ""
				break
			}
			domName = name
			domFreq++
		}
		prev = name
		n++
	}

	// 在没有法律声明的情况下不做任何事情
	if n == 0 {
		return
	}

	// 确定与此decl的值关联的值列表
	values := &r.values
	const threshold = 0.75
	if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {
		// 输入的条目足够频繁
		if typ := r.lookupType(domName); typ != nil {
			values = &typ.values // 与那类人交往
		}
	}

	*values = append(*values, &Value{
		Doc:   decl.Doc.Text(),
		Names: specNames(decl.Specs),
		Decl:  decl,
		order: r.order,
	})
	if r.mode&PreserveAST == 0 {
		decl.Doc = nil // 单据已消耗-从AST中删除
	}
	// 注意：这里使用的顺序是全局的，这一点很重要，因为cleanupTypes
	// 方法可以将与类型关联的值移回全局列表。如果
	// 顺序是特定于列表的，排序是不确定的，因为相同的顺序值
	// 可能出现多次（是错误，在修复#16153时发现）。
	r.order++
}

// 字段返回结构的字段或接口的方法。
// None
func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {
	var fields *ast.FieldList
	switch t := typ.(type) {
	case *ast.StructType:
		fields = t.Fields
		isStruct = true
	case *ast.InterfaceType:
		fields = t.Methods
	}
	if fields != nil {
		list = fields.List
	}
	return
}

// readType处理类型声明。
// None
func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
	typ := r.lookupType(spec.Name.Name)
	if typ == nil {
		return // 无名称或空名称-忽略类型
	}

	// 一个类型最多只能添加一次，因此typ.decl
	// 应该是nil-如果不是，只需覆盖它即可。
	typ.decl = decl

	// 计算文档
	doc := spec.Doc
	if doc == nil {
		// 没有与规范关联的文档，使用声明文档（如果有）
		doc = decl.Doc
	}
	if r.mode&PreserveAST == 0 {
		spec.Doc = nil // 单据已消耗-从AST中删除
		decl.Doc = nil // 单据已消耗-从AST中删除
	}
	typ.doc = doc.Text()

	// 记录匿名字段（它们可能提供方法）
	// （筛选时可能已经记录了某些字段。）
	// 出口，但没关系）
	var list []*ast.Field
	list, typ.isStruct = fields(spec.Type)
	for _, field := range list {
		if len(field.Names) == 0 {
			r.recordAnonymousField(typ, field.Type)
		}
	}
}

// isPredeclared报告n是否表示预先声明的类型。
// None
func (r *reader) isPredeclared(n string) bool {
	return predeclaredTypes[n] && r.types[n] == nil
}

// readFunc处理func或方法声明。
// None
func (r *reader) readFunc(fun *ast.FuncDecl) {
	// 如有要求，剥离功能体。
	if r.mode&PreserveAST == 0 {
		fun.Body = nil
	}

	// 将方法与接收器类型关联（如果有）
	if fun.Recv != nil {
		// 方法
		if len(fun.Recv.List) == 0 {
			// 不应发生（不正确的AST）；（见第17788期）
			// 不要显示此方法
			return
		}
		recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)
		if imp {
			// 不应发生（不正确的AST）；
			// 不要显示此方法
			return
		}
		if typ := r.lookupType(recvTypeName); typ != nil {
			typ.methods.set(fun, r.mode&PreserveAST != 0)
		}
		// 否则忽略该方法
		// TODO（gri）：可能存在非导出类型的导出方法
		// 由于导出的值（常量、变量或
		// 该类型的函数结果）。可以确定这是否是
		// 案例，然后在适当的部分中显示这些方法。
		return
	}

	// 将工厂函数与第一个可见的结果类型关联，只要
	// 其他类型是预先声明的类型。
	if fun.Type.Results.NumFields() >= 1 {
		var typ *namedType // 键入要与之关联的函数
		numResultTypes := 0
		for _, res := range fun.Type.Results.List {
			factoryType := res.Type
			if t, ok := factoryType.(*ast.ArrayType); ok {
				// 我们考虑返回片断或类型数组的函数。
				// T（或指向T的指针）作为T的工厂函数。
				factoryType = t.Elt
			}
			if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) {
				if t := r.lookupType(n); t != nil {
					typ = t
					numResultTypes++
					if numResultTypes > 1 {
						break
					}
				}
			}
		}
		// 如果只有一种结果类型，
		// 将函数与该类型关联。
		if numResultTypes == 1 {
			typ.funcs.set(fun, r.mode&PreserveAST != 0)
			return
		}
	}

	// 只是一个普通函数
	r.funcs.set(fun, r.mode&PreserveAST != 0)
}

var (
	noteMarker    = `([A-Z][A-Z]+)\(([^)]+)\):?`                // 标记（uid），标记至少2个字符，uid至少1个字符
	noteMarkerRx  = lazyregexp.New(`^[ \t]*` + noteMarker)      // 文本开始处的标记（uid）
	noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker) // 注释开始处的标记（uid）
)

// readNote从一系列注释中收集单个注释。
// None
func (r *reader) readNote(list []*ast.Comment) {
	text := (&ast.CommentGroup{List: list}).Text()
	if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {
		// 注释正文在标记之后开始。
		// 我们删除任何格式，这样就不会
		// 在以下情况下获得虚假的换行符/缩进：
		// 显示待办事项正文。
		body := clean(text[m[1]:], keepNL)
		if body != "" {
			marker := text[m[2]:m[3]]
			r.notes[marker] = append(r.notes[marker], &Note{
				Pos:  list[0].Pos(),
				End:  list[len(list)-1].End(),
				UID:  text[m[4]:m[5]],
				Body: body,
			})
		}
	}
}

// readNotes从注释中提取注释。
// 注释必须以“标记（uid）”开头
// 然后是注释正文（例如“
// 注释结束于注释组的末尾或注释组的开头
// 同一评论组中的另一个注释，以先到者为准。
// None
func (r *reader) readNotes(comments []*ast.CommentGroup) {
	for _, group := range comments {
		i := -1 // 最近注释开始的注释索引，如果>=0，则有效
		list := group.List
		for j, c := range list {
			if noteCommentRx.MatchString(c.Text) {
				if i >= 0 {
					r.readNote(list[i:j])
				}
				i = j
			}
		}
		if i >= 0 {
			r.readNote(list[i:])
		}
	}
}

// readFile将源文件的AST添加到读卡器。
// None
func (r *reader) readFile(src *ast.File) {
	// 添加包文档
	if src.Doc != nil {
		r.readDoc(src.Doc)
		if r.mode&PreserveAST == 0 {
			src.Doc = nil // 单据已消耗-从AST中删除
		}
	}

	// 添加除在单独过程中处理的函数之外的所有声明
	for _, decl := range src.Decls {
		switch d := decl.(type) {
		case *ast.GenDecl:
			switch d.Tok {
			case token.IMPORT:
				// 进口是单独处理的
				for _, spec := range d.Specs {
					if s, ok := spec.(*ast.ImportSpec); ok {
						if import_, err := strconv.Unquote(s.Path.Value); err == nil {
							r.imports[import_] = 1
							if s.Name != nil && s.Name.Name == "." {
								r.hasDotImp = true
							}
						}
					}
				}
			case token.CONST, token.VAR:
				// 常量和变量始终作为一个组处理
				r.readValue(d)
			case token.TYPE:
				// 类型是单独处理的
				if len(d.Specs) == 1 && !d.Lparen.IsValid() {
					// 常见情况：不带括号的单声明
					// （如果括号中有单个声明，
					// 在下面创建一个新的假声明，以便
					// go/doc类型声明始终不显示
					// 括号）
					if s, ok := d.Specs[0].(*ast.TypeSpec); ok {
						r.readType(d, s)
					}
					break
				}
				for _, spec := range d.Specs {
					if s, ok := spec.(*ast.TypeSpec); ok {
						// 使用个人（可能是假的）声明
						// 对于每种类型；这也确保了每种类型
						// 获取以（重新）使用声明文档
						// 如果没有与等级库本身关联
						fake := &ast.GenDecl{
							Doc: d.Doc,
							// 不要使用现有的TokPos，因为它
							// 将导致错误的选择范围
							// 如果还有更多的话，伪造的声明
							// 组中有多个类型（这会影响
							// src/cmd/godoc/godoc.go的posLink_urlFunc）
							TokPos: s.Pos(),
							Tok:    token.TYPE,
							Specs:  []ast.Spec{s},
						}
						r.readType(fake, s)
					}
				}
			}
		}
	}

	// 收集标记（…）：注释
	r.readNotes(src.Comments)
	if r.mode&PreserveAST == 0 {
		src.Comments = nil // 已使用的未关联注释-从AST中删除
	}
}

func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
	// 初始化读取器
	r.filenames = make([]string, len(pkg.Files))
	r.imports = make(map[string]int)
	r.mode = mode
	r.types = make(map[string]*namedType)
	r.funcs = make(methodSet)
	r.notes = make(map[string][]*Note)

	// 在读取包文件之前对其进行排序，以便
	// 结果不依赖于映射迭代顺序
	i := 0
	for filename := range pkg.Files {
		r.filenames[i] = filename
		i++
	}
	sort.Strings(r.filenames)

	// 按排序顺序处理文件
	for _, filename := range r.filenames {
		f := pkg.Files[filename]
		if mode&AllDecls == 0 {
			r.fileExports(f)
		}
		r.readFile(f)
	}

	// 处理函数，现在我们有了更好的类型信息
	for _, f := range pkg.Files {
		for _, decl := range f.Decls {
			if d, ok := decl.(*ast.FuncDecl); ok {
				r.readFunc(d)
			}
		}
	}
}

// ----------------------------------------------------------------------------
// 类型

func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {
	if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {
		return f // 不应该发生，但要安全
	}

	// 复制现有接收器字段并设置新类型
	newField := *f.Decl.Recv.List[0]
	origPos := newField.Type.Pos()
	_, origRecvIsPtr := newField.Type.(*ast.StarExpr)
	newIdent := &ast.Ident{NamePos: origPos, Name: recvTypeName}
	var typ ast.Expr = newIdent
	if !embeddedIsPtr && origRecvIsPtr {
		newIdent.NamePos++ // “*”是一个字符
		typ = &ast.StarExpr{Star: origPos, X: newIdent}
	}
	newField.Type = typ

	// 复制现有接收器字段列表并设置新的接收器字段
	newFieldList := *f.Decl.Recv
	newFieldList.List = []*ast.Field{&newField}

	// 复制现有功能声明并设置新的接收器字段列表
	newFuncDecl := *f.Decl
	newFuncDecl.Recv = &newFieldList

	// 复制现有功能文档并设置新声明
	newF := *f
	newF.Decl = &newFuncDecl
	newF.Recv = recvString(typ)
	// 原始场永不改变
	newF.Level = level

	return &newF
}

// collectEmbeddedMethods在mset中收集典型的嵌入方法。
// None
func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {
	visited[typ] = true
	for embedded, isPtr := range typ.embedded {
		// 一旦嵌入类型作为指针类型嵌入
		// 这些类型中的所有嵌入类型都被视为
		// 用于接收器类型的指针类型
		// 计算i、 例如，embeddedIsPtr对此很棘手
		// 嵌入层次结构。
		thisEmbeddedIsPtr := embeddedIsPtr || isPtr
		for _, m := range embedded.methods {
			// 只嵌入顶级方法
			if m.Level == 0 {
				mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))
			}
		}
		if !visited[embedded] {
			r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)
		}
	}
	delete(visited, typ)
}

// computeMethodSets确定遇到的每种类型的实际方法集。
// None
func (r *reader) computeMethodSets() {
	for _, t := range r.types {
		// 收集t的嵌入方法
		if t.isStruct {
			// 结构
			r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))
		} else {
			// 界面
			// TODO（gri）解决此问题
		}
	}

	// 若错误是在本地声明的，则不再将其视为导出字段
	if r.errorDecl {
		for _, ityp := range r.fixlist {
			removeErrorField(ityp)
		}
	}
}

// cleanutypes删除函数和方法与的关联
// 没有声明的类型。相反，这些函数和方法
// 在包级别显示。它还删除缺少的类型
// 声明或不可见的。
// None
func (r *reader) cleanupTypes() {
	for _, t := range r.types {
		visible := r.isVisible(t.name)
		predeclared := predeclaredTypes[t.name]

		if t.decl == nil && (predeclared || visible && (t.isEmbedded || r.hasDotImp)) {
			// t、 名称是预先声明的类型（未在此包中重新声明），
			// 或者它被嵌入了某个地方，但是它的声明丢失了（因为
			// AST不完整），或者我们有一个点导入（并且所有赌注都被取消）：
			// 将所有关联的值、函数和方法移回顶级以便
			// 他们没有迷路。
			// 1） 移动值
			r.values = append(r.values, t.values...)
			// 2） 移动工厂功能
			for name, f := range t.funcs {
				// 在正确的AST中，包级函数名
				// 都不同-无需检查冲突
				r.funcs[name] = f
			}
			// 3） 移动方法
			if !predeclared {
				for name, m := range t.methods {
					// 不要覆盖同名函数-删除它们
					if _, found := r.funcs[name]; !found {
						r.funcs[name] = m
					}
				}
			}
		}
		// 删除不带声明或不可见的类型
		if t.decl == nil || !visible {
			delete(r.types, t.name)
		}
	}
}

// ----------------------------------------------------------------------------
// 分类

type data struct {
	n    int
	swap func(i, j int)
	less func(i, j int) bool
}

func (d *data) Len() int           { return d.n }
func (d *data) Swap(i, j int)      { d.swap(i, j) }
func (d *data) Less(i, j int) bool { return d.less(i, j) }

// sortBy是一个用于排序的辅助函数
func sortBy(less func(i, j int) bool, swap func(i, j int), n int) {
	sort.Sort(&data{n, swap, less})
}

func sortedKeys(m map[string]int) []string {
	list := make([]string, len(m))
	i := 0
	for key := range m {
		list[i] = key
		i++
	}
	sort.Strings(list)
	return list
}

// sortingName返回将d排序到位时使用的名称。
// None
func sortingName(d *ast.GenDecl) string {
	if len(d.Specs) == 1 {
		if s, ok := d.Specs[0].(*ast.ValueSpec); ok {
			return s.Names[0].Name
		}
	}
	return ""
}

func sortedValues(m []*Value, tok token.Token) []*Value {
	list := make([]*Value, len(m)) // 在任何情况下都足够大
	i := 0
	for _, val := range m {
		if val.Decl.Tok == tok {
			list[i] = val
			i++
		}
	}
	list = list[0:i]

	sortBy(
		func(i, j int) bool {
			if ni, nj := sortingName(list[i].Decl), sortingName(list[j].Decl); ni != nj {
				return ni < nj
			}
			return list[i].order < list[j].order
		},
		func(i, j int) { list[i], list[j] = list[j], list[i] },
		len(list),
	)

	return list
}

func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {
	list := make([]*Type, len(m))
	i := 0
	for _, t := range m {
		list[i] = &Type{
			Doc:     t.doc,
			Name:    t.name,
			Decl:    t.decl,
			Consts:  sortedValues(t.values, token.CONST),
			Vars:    sortedValues(t.values, token.VAR),
			Funcs:   sortedFuncs(t.funcs, true),
			Methods: sortedFuncs(t.methods, allMethods),
		}
		i++
	}

	sortBy(
		func(i, j int) bool { return list[i].Name < list[j].Name },
		func(i, j int) { list[i], list[j] = list[j], list[i] },
		len(list),
	)

	return list
}

func removeStar(s string) string {
	if len(s) > 0 && s[0] == '*' {
		return s[1:]
	}
	return s
}

func sortedFuncs(m methodSet, allMethods bool) []*Func {
	list := make([]*Func, len(m))
	i := 0
	for _, m := range m {
		// 确定要包括哪些方法
		switch {
		case m.Decl == nil:
			// 排除冲突条目
		case allMethods, m.Level == 0, !token.IsExported(removeStar(m.Orig)):
			// 强制包含、未嵌入的方法或方法
			// 嵌入但原始接收器类型未导出
			list[i] = m
			i++
		}
	}
	list = list[0:i]
	sortBy(
		func(i, j int) bool { return list[i].Name < list[j].Name },
		func(i, j int) { list[i], list[j] = list[j], list[i] },
		len(list),
	)
	return list
}

// NoteBody返回给定注释列表的注释正文字符串列表。
// 这仅用于填充不推荐使用的Package.Bugs字段。
// None
func noteBodies(notes []*Note) []string {
	var list []string
	for _, n := range notes {
		list = append(list, n.Body)
	}
	return list
}

// ----------------------------------------------------------------------------
// 预先声明的标识符

// IsPredeclared报告s是否为预先声明的标识符。
func IsPredeclared(s string) bool {
	return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s]
}

var predeclaredTypes = map[string]bool{
	"bool":       true,
	"byte":       true,
	"complex64":  true,
	"complex128": true,
	"error":      true,
	"float32":    true,
	"float64":    true,
	"int":        true,
	"int8":       true,
	"int16":      true,
	"int32":      true,
	"int64":      true,
	"rune":       true,
	"string":     true,
	"uint":       true,
	"uint8":      true,
	"uint16":     true,
	"uint32":     true,
	"uint64":     true,
	"uintptr":    true,
}

var predeclaredFuncs = map[string]bool{
	"append":  true,
	"cap":     true,
	"close":   true,
	"complex": true,
	"copy":    true,
	"delete":  true,
	"imag":    true,
	"len":     true,
	"make":    true,
	"new":     true,
	"panic":   true,
	"print":   true,
	"println": true,
	"real":    true,
	"recover": true,
}

var predeclaredConstants = map[string]bool{
	"false": true,
	"iota":  true,
	"nil":   true,
	"true":  true,
}
